#    Tichy
#
#    copyright 2008 Guillaume Chereau (charlie137@gmail.com)
#
#    This file is part of Tichy.
#
#    Tichy is free software: you can redistribute it and/or modify it
#    under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    Tichy is distributed in the hope that it will be useful, but
#    WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#    General Public License for more details.
#
#    You should have received a copy of the GNU General Public License

__docformat__ = 'reStructuredText'

"""Message module"""

import logging
logger = logging.getLogger('Messages')

import tichy
from .attribute import Attribute
from .struct import Struct
from .text import Text
from .peer import Peer
from .time import Time


class Message(Struct):
    """Base class for all messages
    """

    peer = Attribute('peer', Peer)
    text = Attribute('text', Text)
    timestamp = Attribute('timestamp', Time)
    direction = Attribute('direction', Text)
    status = Attribute('status', Text)

    @property
    def fields(self):
        return [Message.peer, Message.text,
                tichy.Action(None, "Send", self._on_send)]

    def __init__(self, peer="", text="", direction='out', status=None,
                 timestamp=None):
        """Create a new message

        :Parameters:

            peer : `TelNumber` | str
                the number / contact of the peer of the message. Its
                __repr__ method will be used as the item's name.

            text : `Text` | unicode
                The text of the message

            direction : str
                the direction of the message. Can be 'in' or 'out'

            status : str
                the status of the message. Can be 'read' or
                'unread'. If set to None, incoming message will have
                'unread' status and outgoing message will have 'read'
                status

            timestamp
                the time at which we received the message. If set to
                None we use the current time
        """
        super(Message, self).__init__()
        self.peer = peer
        self.text = text
        self.text.multilines = True   # XXX: make this better
        self.timestamp = timestamp
        assert direction in ['in', 'out'], direction
        self.direction = direction
        self.status = status or direction == 'out' and 'read' or 'unread'
        assert self.status in ['read', 'unread'], status

    @property
    def title(self):
        return self.peer.title

    @tichy.property
    def subtitle(self):
        # TODO: we could move this into the item class because it is
        #       something we may have ot do for other classes as well.
        value = unicode(self.text).replace('\n', ' ')
        return tichy.Str(value)

    @tichy.property
    def id(self):
        return tichy.Str("%s-%s" % (self.peer, self.timestamp))

    def read(self):
        """Mark the message as read

        This will set the status of the message to 'read' and also
        send the 'read' signal.
        """
        if self.status == 'read':
            return
        self.status = 'read'
        self.emit('read')

    def edit(self, parent=None, **kargs):
        self.read()
        yield super(Message, self).edit(parent, **kargs)

    def create_actor(self):
        """Return an actor on this message"""
        actor = super(Message, self).create_actor()

        def on_details_action(action, msg):
            yield self.view_details()
        details_action = actor.new_action("Details")
        details_action.connect('activated', on_details_action)

        return actor

    def view_details(self, parent=None):
        """return a `Tasklet` that can be used to view the message details

        :Parameters:

            parent : gui.Widget | None
               The widget where we start the application
        """
        editor = EditService().get()
        yield editor.view_details(self, parent)

    def _on_send(self, action):
        yield Sender(None, self)


class MessagesService(tichy.Service):
    """The service that stores all the messages

    This service provides access to the messages inbox and outbox
    """

    def __init__(self):
        self.outbox = tichy.List()
        self.inbox = tichy.List()
        self.database = tichy.Persistance(tichy.phone.SMS, "messages")
        # Prepare the future notification
        self.notification_icon = tichy.Image(self.path(':/message.png'),
                                             (32, 32))
        self.notification = None

    @tichy.tasklet.tasklet
    def init(self):
        logger.info("init")
        messages = self.database.load_all()
        logger.info("got %d messages", len(messages))
        for message in messages:
            self.add(message)
        yield None

    def add(self, msg):
        assert isinstance(msg, Message)
        assert msg.direction in ['in', 'out']
        if msg.direction == 'in':
            self.add_to_inbox(msg)
        elif msg.direction == 'out':
            self.add_to_outbox(msg)

    def add_to_inbox(self, msg):
        """Add a `Message` into the inbox

        :Parameters:
            msg : `Message`
                The message we add
        """
        logger.info("Add to inbox : %s", msg)
        assert(isinstance(msg, Message))
        self.inbox.insert(0, msg)
        self.database.add(msg)
        msg.connect('read', self.on_message_read)
        msg.connect('destroyed', self.on_message_destroyed)
        self._update()

    def add_to_outbox(self, msg):
        """Add a `Message` into the outbox

        :Parameters:
            msg : `Message`
                The message we add
        """
        logger.info("Add to outbox : %s", msg)
        assert(isinstance(msg, Message))
        self.outbox.insert(0, msg)
        self.database.add(msg)

    def on_message_read(self, msg):
        self._update()

    def on_message_destroyed(self, msg):
        self._update()

    def _update(self):
        """Update the notification according to the number of unread messages
        in the inbox.
        """
        nb_unread = len([m for m in self.inbox if m.status == 'unread'])
        logger.debug("%d unread messages", nb_unread)
        if nb_unread == 0 and self.notification:
            self.notification.release()
            self.notification = None
        elif nb_unread > 0 and not self.notification:
            notifications = tichy.NotificationsService.get()
            self.notification = notifications.notify(
                "You have a new message", self.notification_icon)
        elif nb_unread > 0 and self.notification:
            self.notification.msg = "You have %d unread messages" % nb_unread


# XXX: remove this or at least change the name !


class EditService(tichy.Service):

    def edit(self, message, window):
        raise NotImplementedError

    def view_details(self, message, window):
        raise NotImplementedError


# XXX: use a service here ?


class Sender(tichy.Application):
    """Application that runs while we wait for the message to be sent.
    """

    def run(self, parent, sms):
        frame = self.view(parent, title="Sending...")
        vbox = tichy.gui.Box(frame, axis=1, expand=True)
        try:
            yield sms.send()
            yield tichy.Dialog(None, "Sent", "")
        except Exception, e:
            logger.error("Error: %s", e)
            yield tichy.Dialog(None, "Error", e)
